namespace SubhadraSolutions.Sharp.Logging.Context
{
    using SubhadraSolutions.Sharp.Logging.Contracts;
    using SubhadraSolutions.Sharp.Utils;
    using SubhadraSolutions.Sharp.Utils.Diagnostics;
    using System.Collections.Generic;
    using System.Threading;

    public sealed class LoggingContext : AbstractDisposable
    {
        #region Fields

        private static readonly LoggingContext _instance = new LoggingContext();

        private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();
        private readonly HashSet<string> _propertyNames = new HashSet<string>();

        private IClearableGlobalContextStrategy _cStrategy;
        private IGlobalContextStrategy _strategy;

        #endregion Fields

        #region Constructors

        private LoggingContext()
        {
            if (ProcessHelper.CurrentProcessType == ProcessType.ASPDotNetApplication)
            {
                Strategy = new HttpGlobalContextStrategy();
            }
            else
            {
                Strategy = new ThreadLocalStorageGlobalContextStrategy();
                //Strategy=new ThreadDictionaryGlobalContextStrategy();
            }
        }

        #endregion Constructors

        #region Properties

        public static LoggingContext Instance
        {
            get { return _instance; }
        }

        public IGlobalContextStrategy Strategy
        {
            get { return _strategy; }
            set
            {
                _strategy = value;
                _cStrategy = _strategy as IClearableGlobalContextStrategy;
            }
        }

        #endregion Properties

        #region Indexers

        public object this[string name]
        {
            get
            {
                if (name != null)
                {
                    _lock.EnterReadLock();
                    try
                    {
                        return Strategy[name];
                    }
                    finally
                    {
                        _lock.ExitReadLock();
                    }
                }
                return null;
            }
            set
            {
                if (name != null)
                {
                    _lock.EnterWriteLock();
                    try
                    {
                        if (value == null)
                        {
                            Strategy.Remove(name);
                            _propertyNames.Remove(name);
                        }
                        else
                        {
                            Strategy[name] = value;
                            _propertyNames.Add(name);
                        }
                    }
                    finally
                    {
                        _lock.ExitWriteLock();
                    }
                }
            }
        }

        #endregion Indexers

        #region Methods

        public void ClearContext()
        {
            if (_cStrategy != null)
            {
                _cStrategy.Clear();
            }
            else
            {
                try
                {
                    _lock.EnterReadLock();
                    foreach (string propertyName in _propertyNames)
                    {
                        Strategy.Remove(propertyName);
                    }
                }
                finally
                {
                    _lock.ExitReadLock();
                }
            }
        }

        public SortedDictionary<string, object> GetAll(bool excludeNullValues)
        {
            _lock.EnterReadLock();
            try
            {
                if (_propertyNames.Count > 0)
                {
                    var list = new SortedDictionary<string, object>();

                    if (excludeNullValues)
                    {
                        foreach (var propertyName in _propertyNames)
                        {
                            object o = Strategy[propertyName];
                            if (o != null)
                            {
                                list.Add(propertyName, o);
                            }
                        }
                    }
                    else
                    {
                        foreach (var propertyName in _propertyNames)
                        {
                            object o = Strategy[propertyName];

                            list.Add(propertyName, o);
                        }
                    }
                    return list;
                }
                return null;
            }
            finally
            {
                _lock.ExitReadLock();
            }
        }

        public void Remove(string name)
        {
            if (name != null)
            {
                Strategy.Remove(name);
            }
        }

        protected override void Dispose(bool disposing)
        {
            _lock.Dispose();
        }

        #endregion Methods
    }
}